#include "ArchValidator.hpp"
#include <string>
#include <math.h>
#include <boost/regex.hpp>
#include <iostream>
#include <fstream>

using namespace std;

#define AV_DONT_CARE	2

ArchValidator::ArchValidator(GAmodSettings &settings,error_hand_type eht) throw(ArchValidatorException)
{
    //const GAmodStrSetting *strsett;
    TemplateGAmodObjWrapper<string> *strsett;
    const char *tmp;
    string tmpstr,newDataFile;
	boost::regex  pat( "^(?:((?:.+?)\\.txt);)+$");
	boost::smatch matches;
	unsigned i;

	strsett = (TemplateGAmodObjWrapper<string>*)settings.get_setting("n_eval");
	tmp = (strsett) ? strsett->value.c_str() : NULL;

	if(tmp)
	{
		n_eval = atoi(tmp);
        //free((void*)tmp);
        IF_DBG_VERB(printf("[INFO] ArchValidator::ArchValidator: n_evals <%s> = %d\n",tmp,n_eval);)

		if(n_eval<=0)
			throw ArchValidatorException("ArchValidator::ArchValidator: valore di n_evals errato");
	}
	else
	{
		IF_DBG_VERB(printf("[WARN] ArchValidator::ArchValidator: n_eval non specificato, assumo valore stardard (1)\n");)
		n_eval = 1;
	}

	evalT   = AV_EVAL_SEQ;

	strsett = (TemplateGAmodObjWrapper<string>*)settings.get_setting("eval_type");
	tmp = (strsett) ? strsett->value.c_str() : NULL;

	if(tmp)
	{

        //free((void*)tmp);
        IF_DBG_VERB(printf("[INFO] ArchValidator::ArchValidator: eval_type <%s> = ",tmp);)
		if(strcmp(tmp,"seq")==0)
		{
			IF_DBG_VERB(printf("AV_EVAL_SEQ\n");)
			evalT  = AV_EVAL_SEQ;
			//n_eval = 1;
		}
		if(strcmp(tmp,"rnd")==0)
		{
			IF_DBG_VERB(printf("AV_EVAL_RND\n");)
			evalT  = AV_EVAL_RND;
		}

		if(n_eval<=0)
			throw ArchValidatorException("ArchValidator::ArchValidator: valore di n_evals errato");
	}
	else
	{
		IF_DBG_VERB(printf("[WARN] ArchValidator::ArchValidator: eval_type non specificato, assumo valore stardard (AV_EVAL_SEQ)\n");)
	}

	strsett = (TemplateGAmodObjWrapper<string>*)settings.get_setting("train_set");
	tmp = (strsett) ? strsett->value.c_str() : NULL;
	if(!tmp)
        throw ArchValidatorException("ArchValidator::ArchValidator: impossibile trovare 'train_set'");

    tmpstr.assign(tmp);
	this->max_score = 0;

	if (boost::regex_match(tmpstr, matches, pat,boost::match_extra))
	{
		for(i = 0; i < matches.captures(1).size(); ++i)
		{
			newDataFile = (string)matches.captures(1)[i].str();
			IF_DBG_VERB(printf("[INFO] ArchValidator::ArchValidator: Parsing [%s]...\n",newDataFile.c_str());)
			try
			{
				max_score += parseInputFile(newDataFile);
			}
			catch(ArchValidatorException &e)
			{
				throw e;
			}
		}
		//max_score = data_vector.size() * DEF_ROWS * n_eval;
		max_score *= n_eval;
	}
	else
		throw ArchValidatorException("ArchValidator::ArchValidator: impossibile effettuare il parsing di 'train_set'");


    IF_DBG_VERB(printf("[INFO] ArchValidator::ArchValidator: Aggiunte %d entry\n",(int)data_vector.size());)
    IF_DBG_VERB(printf("[INFO] ArchValidator::ArchValidator: max score =  %d \n", this->max_score);)

	if (data_vector.size() == 0)
		throw ArchValidatorException("ArchValidator::ArchValidator: Nessun vettore di test aggiunto");

	return;
}

int ArchValidator::parseInputFile(string &filePath) throw(ArchValidatorException)
{

	string			line,tmpString;
   	boost::regex	pat( "^\\[(?:([01]),?)+]>\\[(?:([01\\*]),?)+]$" );
	unsigned		i,lineCount;
	vector<HERAuint8>	inArray,outArray;
	HERAuint8			*tmpArray;
	int				scoreBits = 0;

	ifstream inFile(filePath.c_str());
	if (!inFile.is_open())
		exit(1);

	lineCount = 0;
    while (!inFile.eof())
    {
		//line.clear();
		++lineCount;
		inArray.clear();
		outArray.clear();
        getline(inFile, line);
		boost::smatch matches;
        if (boost::regex_search(line, matches, pat,boost::match_extra))
        {
			IF_DBG_VERB(printf("Input: [");)
				for(i = 0; i < matches.captures(1).size(); ++i)
				{
					tmpString = (string)matches.captures(1)[i].str();
					IF_DBG_VERB( if (i) printf(","); printf("%s,",tmpString.c_str());)
					inArray.push_back(atoi(tmpString.c_str()));
					//IF_DBG_INFO(printf("[INFO] ArchValidator::ArchValidator: Parsing [%s]...\n",newDataFile.c_str());)
				}

			IF_DBG_VERB(printf("] Output: [");)
				for(i = 0; i < matches.captures(2).size(); ++i)
				{

					tmpString = (string)matches.captures(2)[i].str();
					IF_DBG_VERB( if (i) printf(","); printf("%s",tmpString.c_str());)
					if(tmpString[0]!='*')
					{
						outArray.push_back(atoi(tmpString.c_str()));
						scoreBits++;
					}
					else
					{
						outArray.push_back(AV_DONT_CARE);
					}
					//IF_DBG_INFO(printf("[INFO] ArchValidator::ArchValidator: Parsing [%s]...\n",newDataFile.c_str());)
				}

			IF_DBG_VERB(printf("]\n");)

			if(outArray.size() == DEF_ROWS && inArray.size() == DEF_ROWS)
			{
				tmpArray = (HERAuint8*)malloc(2*DEF_ROWS*sizeof(HERAuint8));
				//TODO: Gestire allocazione mancata
				for(i=0; i<DEF_ROWS; i++)
				{
					tmpArray[i]					= inArray[i];
					tmpArray[i+DEF_ROWS]		= outArray[i];
				}
				this->data_vector.push_back(tmpArray);
			}
			else
			{
				IF_DBG_VERB(printf("[WARN] ArchValidator::parseInputFile: Numero di segnali errato @[%d]\n",lineCount);)
			}

		}
	}
	inFile.close();

	return scoreBits;
}

ArchValidator::~ArchValidator()
{
	vector<HERAuint8*>::iterator it_data;

	for(it_data = data_vector.begin();it_data!=data_vector.end();it_data++)
	{
		free((HERAuint8*)(*it_data));
	}
}


unsigned int ArchValidator::valuta_pop(Popolazione *pop,stat_generation sg) throw(ArchValidatorException)
{
	try{
		return valuta_pop(pop,0,0,sg);
	}
	catch(ArchValidatorException &e)
	{
		throw e;
	}
}

unsigned int ArchValidator::valuta_pop(Popolazione *pop, int eval_from, int eval_to,stat_generation sg) throw(ArchValidatorException)
{

	vector<HERAuint8*>::iterator it_data;
	int n_ind,pop_size,score,i,evalN;
	HERAuint8		result;
	int				iterCount;
	unsigned int	bestScore;

	if(!pop)
		throw ArchValidatorException("ArchValidator::valuta_pop: Popolazione non valida");

	pop_size = pop->get_size();
	if(eval_to == 0)
		eval_to = pop_size-1;

	//bestScore = -1;
	bestScore=0;
	for(n_ind = eval_from; n_ind<=eval_to; n_ind++)
	{
		IF_DBG_VERB_DBG(printf("------INVIDIDUO %d-------\n",n_ind);)
		if((*pop)[n_ind].mod == 0)	continue;
		//VALIDAZIONE
		(*pop)[n_ind].reset_FF();

		score			= 0;
		iterCount		= 0;

		for(evalN = 0 ; evalN<n_eval; evalN++)
		{

			if(evalT == AV_EVAL_RND)
				scrambleData();

			for(it_data = data_vector.begin(); it_data!=data_vector.end(); it_data++)
			{
			#ifdef INJECT_ERRORS
				(*pop)[n_ind].injectAllErrors();
			#endif
				result = (HERAuint8)(*pop)[n_ind].simulate_run((op_type*)*it_data,0);


				IF_DBG_VERB_DBG(
					printf("In: [");
					for(i=0; i<DEF_ROWS; i++)
					{
						if(i) printf(", ");
						printf("%d", (*it_data)[i]);
					}
					printf("]\n");

					printf("Out: [");
					for(i=0; i<DEF_ROWS; i++)
					{
						if(i) printf(", ");
						printf("%d", (*it_data)[DEF_ROWS+i]);
					}
					printf("]\n");
				)
				for(i=0; i<DEF_ROWS; i++)
				{
					if ((*it_data)[2*DEF_ROWS-1-i] == AV_DONT_CARE)
						continue;
					else
						if(result & (1 << i))
						{
							if ((*it_data)[2*DEF_ROWS-1-i])
								score++;
						}
						else
							if (!(*it_data)[2*DEF_ROWS-1-i])
								score++;
				}


				IF_DBG_VERB_DBG(printf("Score: %d",score); getchar();)
				//printf("Score: %d\n",score);
			}
		}


		(*pop)[n_ind].fitness = score;
		(*pop)[n_ind].mod = 0;
		if (score > bestScore) bestScore = score;
	}

	return bestScore;
}


unsigned int ArchValidator::snapIndividuo(Individuo &Ind,char *path) throw(ArchValidatorException)
{
	vector<HERAuint8*>::iterator it_data;
	//unsigned int n_ind,pop_size,score,new_score,n_trials;
	char		stringa[255];
	int			i;
	ofstream	outFile;
	HERAuint8		result;

	sprintf(stringa,"%s.txt",path);
	outFile.open(stringa);

	if (!outFile.is_open())
		throw ArchValidatorException("ArchValidator::snapIndividuo: impossibile aprire il file per il dump");

	Ind.reset_FF();

#ifdef INJECT_ERRORS
	Ind.injectAllErrors();
#endif

	for(it_data = data_vector.begin(); it_data != data_vector.end(); it_data++)
	{
		outFile << "[";
		for(i=0; i<DEF_ROWS; i++)
		{
			if(i)
				outFile << ",";
			outFile << (int)(*it_data)[i];
		}
		outFile << "]>[";
		result = (HERAuint8)Ind.simulate_run((op_type*)*it_data,0);
		for(i=0; i<DEF_ROWS; i++)
		{
			if(i) outFile << ",";
			if(result & (1 << (DEF_ROWS - i -1)))
				outFile << "1";
			else
				outFile << "0";
		}
		outFile << "]\n";
	}

	outFile.close();

	return 0;
}

void ArchValidator::scrambleData()
{
	HERAuint8 *tmp;
	int i,randPos,vsize;

	vsize = data_vector.size();
	//printf("\n>scramble<\n");
	for(i=0; i<vsize; i++)
	{
		randPos = i + (rand() % (vsize-i));
		if(randPos != i)
		{
			tmp = data_vector[i];
			data_vector[i] = data_vector[randPos];
			data_vector[randPos] = tmp;
		}
	}
}
